package org.grayrabbit.commons.model;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

/**
 * 通用Mybatis pojo对象
 * 
 * @author sys53
 * @version $Id: MyBatisPojo.java, v 0.1 2012-4-19 下午3:32:45 sys53 Exp $
 */
public class MyBatisPojo implements Serializable {

    private static final long serialVersionUID = -794481383512737065L;

    /**包含空字段*/
    private transient String[] includeNullColumns = new String[0];

    /**
     * 设置包含更新的字段
     * 
     * @param includeNullColumns
     */
    public void setIncludeNullColumns(String...includeNullColumns) {
        this.includeNullColumns = includeNullColumns;
    }

    /**
     * 获取POJO对应的表名 需要POJO中的属性定义@Table(name)
     * 
     * @return
     */
    public String tablename() {
        Table table = this.getClass().getAnnotation(Table.class);
        if (table != null)
            return table.name();
        else
            throw new RuntimeException("undefine POJO @Table, need Tablename(@Table)");
    }

    /**
     * 获取POJO对应的主键名称 需要POJO中的属性定义@Id
     * 
     * @return
     */
    public String id() {
        for (Field field : this.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class))
                return field.getName();
        }
        //找祖宗的id
        return getPerentId(this.getClass().getSuperclass());

    }

    /**
     * 继承类的话，查询父类的主键
     * 
     * @return
     */
    private String getPerentId(Class<?> clazz) {
        if (Object.class.equals(clazz)) {
            //到了Object都还没有找到id，说明id未设置
            throw new RuntimeException("undefine POJO @Id");
        }
        //找父类的id
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class))
                return field.getName();
        }
        //继续找父类的父类id
        return getPerentId(clazz.getSuperclass());
    }

    /**
     * 获取POJO对应主键对应的数据库字段名称
     * 
     * @return
     */
    public String idColumnName() {
        for (Field field : this.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                if (field.isAnnotationPresent(Column.class)) {
                    Column c = field.getAnnotation(Column.class);
                    if (StringUtils.isNotBlank(c.name())) {
                        return c.name();
                    }
                }
                return field.getName();
            }
        }
        //继续找父类的父类column name
        return getPerentIdColumnName(this.getClass().getSuperclass());
    }

    /**
     * 继承类的话，查询父类的主键
     * 
     * @return
     */
    private String getPerentIdColumnName(Class<?> clazz) {
        if (Object.class.equals(clazz)) {
            //到了Object都还没有找到id，说明id未设置
            throw new RuntimeException("undefine POJO @Id");
        }
        //找父类的id
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                if (field.isAnnotationPresent(Column.class)) {
                    Column c = field.getAnnotation(Column.class);
                    if (StringUtils.isNotBlank(c.name())) {
                        return c.name();
                    }
                }
                return field.getName();
            }
        }
        //继续找父类的父类id
        return getPerentIdColumnName(clazz.getSuperclass());
    }

    /** 
     * 用于存放POJO的列信息 
     */
    private transient static Map<Class<? extends MyBatisPojo>, List<String>> columnMap = new HashMap<Class<? extends MyBatisPojo>, List<String>>();

    private boolean isNull(String fieldname) {
        try {
            Field field = this.getClass().getDeclaredField(fieldname);
            return isNull(field);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }

        return false;
    }

    private boolean isNull(Field field) {
        try {
            field.setAccessible(true);
            return field.get(this) == null;
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return false;
    }

    private String getColumnName(String fieldName) {
        try {
            Field field = this.getClass().getDeclaredField(fieldName);
            Column c = field.getAnnotation(Column.class);
            if (StringUtils.isEmpty(c.name())) {
                return field.getName();
            } else {
                return c.name();
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }

    /** 
     * 用于计算类定义 
     * 需要POJO中的属性定义@Column(name) 
     */
    public void caculationColumnList() {
        if (columnMap.containsKey(this.getClass()))
            return;

        Field[] fields = this.getClass().getDeclaredFields();
        List<String> columnList = new ArrayList<String>(fields.length);

        for (Field field : fields) {
            if (field.isAnnotationPresent(Column.class)) {
                columnList.add(field.getName());
            }
        }

        columnMap.put(this.getClass(), columnList);
    }

    /** 
     * 获取用于WHERE的 有值字段表 
     * @return 
     */
    public List<WhereColumn> returnWhereColumnsName() {
        Field[] fields = this.getClass().getDeclaredFields();
        List<WhereColumn> columnList = new ArrayList<WhereColumn>(fields.length);

        for (Field field : fields) {
            if (field.isAnnotationPresent(Column.class) && !isNull(field))
                columnList.add(new WhereColumn(field.getName(), field.getGenericType().equals(
                    String.class)));
        }

        return columnList;
    }

    /** 
     * Where条件信息 
     * @author sys53 
     * 
     */
    public class WhereColumn {
        public String name;
        public boolean isString;

        public WhereColumn(String name, boolean isString) {
            this.name = name;
            this.isString = isString;
        }
    }

    /** 
     * 用于获取Insert的字段累加 
     * @return 
     */
    public String returnInsertColumnsName() {
        StringBuilder sb = new StringBuilder();
        List<String> list = columnMap.get(this.getClass());
        int i = 0;
        for (String column : list) {
            if (!ArrayUtils.contains(includeNullColumns, column) && isNull(column))
                continue;
            String columnName = getColumnName(column);
            if (i++ != 0)
                sb.append(',');
            sb.append(columnName);
        }
        return sb.toString();
    }

    /** 
     * 用于获取Insert的字段映射累加 
     * @return 
     */
    public String returnInsertColumnsDefine() {
        StringBuilder sb = new StringBuilder();
        List<String> list = columnMap.get(this.getClass());
        int i = 0;
        for (String column : list) {
            if (!ArrayUtils.contains(includeNullColumns, column) && isNull(column))
                continue;
            if (i++ != 0)
                sb.append(',');
            sb.append("#{").append(column).append('}');
        }
        return sb.toString();
    }

    /** 
     * 用于获取Update Set的字段累加 
     * @return 
     */
    public String returnUpdateSet() {
        StringBuilder sb = new StringBuilder();
        List<String> list = columnMap.get(this.getClass());
        int i = 0;
        for (String column : list) {
            if (!ArrayUtils.contains(includeNullColumns, column) && isNull(column))
                continue;
            String columnName = getColumnName(column);
            if (i++ != 0)
                sb.append(',');
            sb.append(columnName).append("=#{").append(column).append('}');
        }
        return sb.toString();
    }

    public Integer getId() {
        return 0;
    }

}
